﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using App1AppHandler;
using System.Data.SqlClient;

namespace Reborn_WorldServer.App1AppHandler
{
    public class RecipieManager
    {
        private Character character;

        private List<Recipie> recipies = new List<Recipie>();

        public RecipieManager(Character character)
        {
            this.character = character;

            //LOAD ITEM IDs
            SqlConnection MyCon = new SqlConnection(Managers.DBConnectionString);
            SqlCommand SQLCom = new SqlCommand("Characters_Recipies_Select", MyCon);
            SQLCom.CommandType = System.Data.CommandType.StoredProcedure;
            SQLCom.Parameters.AddWithValue("@CharacterID", character.ID);

            MyCon.Open();
            SqlDataReader SDR = SQLCom.ExecuteReader();

            lock (recipies)
            {
                while (SDR.Read())
                {
                    recipies.Add(Managers.GameHandler.Recipies[int.Parse(SDR["RecipieID"].ToString())]);
                }
            }

            SDR.Close();
            MyCon.Close();
            MyCon.Dispose();
        }

        public List<Recipie> getRecipiesCpy(string tradeSkillType)
        {
            List<Recipie> recipiesCpy = new List<Recipie>();

            lock (recipies)
            {
                foreach (Recipie r in recipies)
                {
                    if (r.Skill == tradeSkillType || tradeSkillType == "")
                        recipiesCpy.Add(r);
                }
            }

            return recipiesCpy;
        }


        public void createRecipie(int recipieId)
        {
            Recipie recipieToCreate = null;
            lock (recipies)
            {
                foreach (Recipie recipie in recipies)
                {
                    if (recipie.ID == recipieId)
                    {
                        recipieToCreate = recipie;
                        break;
                    }
                }
            }

            

            //Send Error (Should not get here)
            if (recipieToCreate == null)
            {
                character.PlaySound("Ding", character.MapX, character.MapY);
                character.SendMessage("ERROR: You do not own the selected recipie! Please bug report!", "server");

                //This will re-enable the trade skill window since they are disabled pending a response
                character.ClientSocket.socket.BeginSend("<recipieresponse />");
                return;
            }

            //Try for successfull item creation.
            int recipieLevelDiff = recipieToCreate.ReqLevel - System.Convert.ToInt32(Math.Floor(character.skillManager.GetCurrentLevel(recipieToCreate.Skill)));

            //Should not get here, character should not have access to recipies more than 2 levels above
            if (recipieLevelDiff > 2)
            {
                character.PlaySound("Ding", character.MapX, character.MapY);
                character.SendMessage("Your skill is not high enough to attempt this recipie!", "server");

                //This will re-enable the trade skill window since they are disabled pending a response
                character.ClientSocket.socket.BeginSend("<recipieresponse />");
                return;
            }


            List<Loot> yeildsItems = new List<Loot>();
            foreach (RecipieItem recipieItem in recipieToCreate.yeildsItems)
            {
                for (int x = 0; x < recipieItem.Qty; x++)
                    yeildsItems.Add(recipieItem.Item);
            }
            //Check for inventory error
            if (character.HasInventoryFor(yeildsItems) == false)
            {
                character.PlaySound("Ding", character.MapX, character.MapY);
                character.SendMessage("You do not have enough inventory space!", "server");

                //This will re-enable the trade skill window since they are disabled pending a response
                character.ClientSocket.socket.BeginSend("<recipieresponse />");
                return;
            }
            //Check for Lore item error
            foreach (Loot item in yeildsItems)
            {
                if (item.Lore == true && character.hasItem(item.ID))
                {
                    character.SendMessage("You cannot carry any more " + item.Name, "server");
                    character.PlaySound("Ding", character.MapX, character.MapY);

                    //This will re-enable the trade skill window since they are disabled pending a response
                    character.ClientSocket.socket.BeginSend("<recipieresponse />");

                    return;
                }
            }

            List<Loot> inventoriedRequiredItems = hasRecipieItems(recipieToCreate);
            //Should not get here, the client should not allow creation request if we dont have all reagents.
            if (inventoriedRequiredItems == null)
            {
                character.PlaySound("Ding", character.MapX, character.MapY);
                character.SendMessage("You do not have all of the required items! Please bug report!", "server");

                //This will re-enable the trade skill window since they are disabled pending a response
                character.ClientSocket.socket.BeginSend("<recipieresponse />");
                return;
            }

            //Not all required items will be consumed, modify the inventoriedRequiredItems so we only remove the consumable reagents
            foreach (RecipieItem recipieItem in recipieToCreate.requiredItems)
            {
                for (int x = 0; x < recipieItem.Qty; x++)
                {
                    //If this recipie item is not consumed, remove it from the inventoried item list
                    if (recipieItem.IsConsumed == false)
                    {
                        foreach (Loot loot in inventoriedRequiredItems)
                        {
                            if (loot.ID == recipieItem.Item.ID)
                            {
                                inventoriedRequiredItems.Remove(loot);
                                break;
                            }
                        }
                    }
                }
            }

            //Remove the required items from characters inventory
            bool updateCharacter1 = false;
            bool updateBag1 = false;
            bool updateBelt1 = false;
            character.RemoveFromInventory(inventoriedRequiredItems, out updateBag1, out updateBelt1, out updateCharacter1, true);

            //Values used later on for AddToInventory() only if successfull
            bool updateCharacter2 = false;
            bool updateBag2 = false;
            bool updateBelt2 = false;

            //Depending on our level vs recipie level, this value is the maximum we can random 1/100 for success
            int maxSuccessValue = 0;
            int expToAdd = 0;

            //Anything more than 3 levels below us is treated the same.
            if (recipieLevelDiff < -2)
            {
                maxSuccessValue = 95;
                expToAdd = 0;
            }
            else if (recipieLevelDiff == -2)
            {
                maxSuccessValue = 90;
                expToAdd = 25;          //90% 22
            }
            else if (recipieLevelDiff == -1)
            {
                maxSuccessValue = 75;
                expToAdd = 38;          //78% 28
            }
            else if (recipieLevelDiff == 0)
            {
                maxSuccessValue = 50;
                expToAdd = 70;              //50% 35
            }
            else if (recipieLevelDiff == 1)
            {
                maxSuccessValue = 25;
                expToAdd = 110;             //25% 27
            }
            else if (recipieLevelDiff == 2)
            {
                maxSuccessValue = 5;
                expToAdd = 400;         //5% 20
            }

            int randomSelection = Managers.GameHandler.random.Next(1, 101);

            bool isSuccessfull = false;
            if (randomSelection <= maxSuccessValue)
                isSuccessfull = true;

            if (isSuccessfull)
            {
                //Create the new items from the master
                for (int x = 0; x < yeildsItems.Count; x++)
                    yeildsItems[x] = Managers.GameHandler.CreateItem(yeildsItems[x].ID);

                //Add new items to players inventory
                character.AddToInventory(yeildsItems, out updateBag2, out updateBelt2, out updateCharacter2);

                //Update Exp
                if (expToAdd > 0)
                    character.skillManager.AddExp(recipieToCreate.Skill, System.Convert.ToDecimal(expToAdd));

                character.SendMessage("SUCCESS: " + recipieToCreate.Name, "server");
            }
            else
            {
                character.SendMessage("FAILED: " + recipieToCreate.Name, "server");
            }

            if (updateCharacter1 || updateCharacter2)
                character.Update_Character();
            if (updateBag1 || updateBag2)
                character.bagManager.UpdateBag();
            if (updateBelt1 || updateBelt2)
                character.beltManager.UpdateBelt();

            List<Recipie> effectedRecipies = getRelatedRecipies(recipieToCreate);

            //Update clients trade skill window.
            character.ClientSocket.socket.BeginSend("<recipieresponse>" + getTradeSkillData(recipieToCreate.Skill, false, effectedRecipies) + "</recipieresponse>");
        }

        //Gets recipies with related items.
        public List<Recipie> getRelatedRecipies(Recipie relatedTo)
        {
            List<Recipie> result = new List<Recipie>();
            result.Add(relatedTo);

            lock (recipies)
            {
                //Iterate through known recipies
                foreach (Recipie recipie in recipies)
                {
                    if (recipie.ID != relatedTo.ID)
                    {
                        bool isRelated = false;

                        foreach (RecipieItem recipieItem in recipie.requiredItems)
                        {
                            foreach (RecipieItem relatedItem in relatedTo.yeildsItems)
                            {
                                if (relatedItem.Item.ID == recipieItem.Item.ID)
                                {
                                    isRelated = true;
                                    break;
                                }
                            }

                            if (isRelated == true)
                                break;

                            foreach (RecipieItem relatedItem in relatedTo.requiredItems)
                            {
                                if (relatedItem.Item.ID == recipieItem.Item.ID)
                                {
                                    isRelated = true;
                                    break;
                                }
                            }

                            if (isRelated == true)
                                break;
                        }

                        if (isRelated == true)
                            result.Add(recipie);
                    }
                }
            }

            return result;
        }

        public bool hasRecipie(Recipie recipie)
        {
            lock (recipies)
            {
                return recipies.Contains(recipie);
            }
        }

        private List<Loot> hasRecipieItems(Recipie recipie)
        {
            List<Loot> result = new List<Loot>();

            foreach (RecipieItem recipieItem in recipie.requiredItems)
            {
                Loot requiredItem = recipieItem.Item;

                for (int z = 0; z < recipieItem.Qty; z++)
                {
                    Loot leftHandItem = character.LeftHand;
                    if (leftHandItem != null && leftHandItem.ID == requiredItem.ID && (leftHandItem.BoundTo == -1 || leftHandItem.BoundTo == character.ID) && result.Contains(leftHandItem) == false)
                    {
                        result.Add(leftHandItem);
                        continue;
                    }

                    Loot rightHandItem = character.RightHand;
                    if (rightHandItem != null && rightHandItem.ID == requiredItem.ID && (rightHandItem.BoundTo == -1 || rightHandItem.BoundTo == character.ID) && result.Contains(rightHandItem) == false)
                    {
                        result.Add(rightHandItem);
                        continue;
                    }

                    Loot[] beltItems = character.beltManager.getBeltLootCopy();
                    bool foundInBelt = false;
                    for (int x = 0; x < beltItems.Length; x++)
                    {
                        Loot beltLoot = beltItems[x];
                        if (beltLoot != null && beltLoot.ID == requiredItem.ID && (beltLoot.BoundTo == -1 || beltLoot.BoundTo == character.ID) && result.Contains(beltLoot) == false)
                        {
                            result.Add(beltLoot);
                            foundInBelt = true;
                            break;
                        }
                    }
                    if (foundInBelt == true)
                        continue;

                    List<Loot> bagItems = character.bagManager.getAllBagItems();
                    bool foundInBag = false;
                    for (int x = 0; x < bagItems.Count; x++)
                    {
                        Loot bagLoot = bagItems[x];
                        if (bagLoot != null && bagLoot.ID == requiredItem.ID && (bagLoot.BoundTo == -1 || bagLoot.BoundTo == character.ID) && result.Contains(bagLoot) == false)
                        {
                            result.Add(bagLoot);
                            foundInBag = true;
                            break;
                        }
                    }
                    if (foundInBag == true)
                        continue;

                    List<Loot> bankItems = character.bankManager.getBankCopy();
                    bool foundInBank = false;
                    for (int x = 0; x < bankItems.Count; x++)
                    {
                        Loot bankLoot = bankItems[x];
                        if (bankLoot != null && bankLoot.ID == requiredItem.ID && (bankLoot.BoundTo == -1 || bankLoot.BoundTo == character.ID) && result.Contains(bankLoot) == false)
                        {
                            result.Add(bankLoot);
                            foundInBank = true;
                            break;
                        }
                    }
                    if (foundInBank == true)
                        continue;

                    return null;
                }
            }

            return result;
        }
        
        private RecipieData getRecipieData(Recipie recipie)
        {
            //Number of times we can fasion this recipie
            int availableCombines = -1;

            RecipieData rd = new RecipieData();
            rd.copyFrom(recipie);

            //Asssign our available quantities to required items
            foreach (RecipieItemData rid in rd.requiredItems)
            {
                rid.avail = character.countInventory(rid.Item.ID, true);

                //Record the lowest number of possible combines per our inventoried required items
                int numCombines = rid.avail / rid.Qty;
                if (availableCombines == -1 || numCombines < availableCombines)
                    availableCombines = numCombines;
            }

            if (availableCombines == -1)
                availableCombines = 0;

            rd.avail = availableCombines;

            return rd;
        }
        public string getTradeSkillData(string tradeType, bool sendToClient)
        {
            return getTradeSkillData(tradeType, sendToClient, null);
        }
        public string getTradeSkillData(string tradeType, bool sendToClient, List<Recipie> forRecipies)
        {
            string result = "";

            TradeSkillData tradeSkillData = new TradeSkillData();
            List<Recipie> recipies = new List<Recipie>();

            tradeSkillData.skill = tradeType;
            tradeSkillData.level = character.skillManager.GetCurrentLevel(tradeType);

            recipies = getRecipiesCpy(tradeType);
            recipies.Sort();

            if (forRecipies != null)
            {
                foreach (Recipie r in forRecipies)
                {
                    tradeSkillData.recipies.Add(getRecipieData(r));
                }
            }
            else
            {
                //Change our Recipie objects to RecipieData objects for client
                foreach (Recipie r in recipies)
                {
                    tradeSkillData.recipies.Add(getRecipieData(r));
                }
            }

            result = tradeSkillData.toXml(character);

            if (sendToClient)
            {
                character.ClientSocket.socket.BeginSend(result);
            }

            return result;
        }

        public bool addRecipie(Recipie recipie)
        {
            lock (recipies)
            {
                if (recipies.Contains(recipie) == false)
                {
                    recipies.Add(recipie);
                    recipiesChanged = true;

                    return true;
                }
            }

            return false;
        }

        public bool hasSkill(string type)
        {
            if (character.skillManager.GetCurrentLevel(type) > 0)
                return true;

            return false;
        }

        public void learnSkill(string type, long trainerCreatureId)
        {
            string errorMessage;
            if (canLearn(out errorMessage) == false)
            {
                Managers.GameHandler.TrainerOpenWindow(character, trainerCreatureId);
                return;
            }

            character.skillManager.AddExp(type, 1);
            Managers.GameHandler.TrainerOpenWindow(character, trainerCreatureId);

            character.SendMessage("You learn " + type + "!", "server");
        }

        public void forgetSkill(string type, long trainerCreatureId)
        {
            lock (recipies)
            {
                for (int x = 0; x < recipies.Count; x++)
                {
                    if (recipies[x].Skill == type)
                    {
                        recipies.RemoveAt(x);
                        x--;
                    }
                }
            }
            recipiesChanged = true;

            character.skillManager.removeSkillLevel(type);
            Managers.GameHandler.TrainerOpenWindow(character, trainerCreatureId);

            character.SendMessage("You forget " + type + "!", "server");
        }

        private bool recipiesChanged = false;
        public void saveRecipies()
        {
            if (recipiesChanged == false)
                return;
            else
                recipiesChanged = false;

            List<Recipie> recipiesToSave = getRecipiesCpy("");

            SqlConnection MyCon = new SqlConnection(Managers.DBConnectionString);
            MyCon.Open();
            SqlTransaction MyTrans = MyCon.BeginTransaction();

            SqlCommand SQLCom = new SqlCommand();
            SQLCom.Connection = MyCon;
            SQLCom.CommandType = System.Data.CommandType.StoredProcedure;
            SQLCom.Transaction = MyTrans;

            try
            {
                //Delete existing items from bank
                SQLCom.CommandText = "Characters_Recipies_Delete";
                SQLCom.Parameters.AddWithValue("@CharacterID", character.ID);
                SQLCom.ExecuteNonQuery();

                //Add new items to bank
                SQLCom.CommandText = "Characters_Recipies_Insert";
                //Character ID already exists from above query
                SQLCom.Parameters.Add("@RecipieID", System.Data.SqlDbType.Int);
                foreach (Recipie recipie in recipiesToSave)
                {
                    SQLCom.Parameters["@RecipieID"].Value = recipie.ID;

                    SQLCom.ExecuteNonQuery();
                }

                MyTrans.Commit();
            }
            catch
            {
                MyTrans.Rollback();
            }
            finally
            {
                MyCon.Close();
                MyCon.Dispose();
            }
        }

        public bool canLearn(out string errorMessage)
        {
            errorMessage = "";

            int characterMaxMeleeOrMagicSkill = System.Convert.ToInt32(Math.Floor(character.skillManager.getMaxSkill()));

            List<string> knownTradeSkills = new List<string>();
            lock (recipies)
            {
                foreach (Recipie recipie in recipies)
                {
                    if (knownTradeSkills.Contains(recipie.Skill) == false)
                        knownTradeSkills.Add(recipie.Skill);
                }
            }

            if (knownTradeSkills.Count >= 2)
            {
                errorMessage = "You cannot learn any more trade skills.";
                return false;
            }
            if (knownTradeSkills.Count == 1)
            {
                if (characterMaxMeleeOrMagicSkill >= 20)
                    return true;
                else
                {
                    errorMessage = "You cannot learn another trade skill until you have any non-trade skill to level 20.";
                    return false;
                }
            }
            else if (knownTradeSkills.Count == 0)
            {
                //if (characterMaxMeleeOrMagicSkill >= 10)
                    return true;
                //else
                //{
                //    errorMessage = "You cannot learn a trade skill until you have any skill to level 10.";
                //    return false;
                //}
            }

            return false;
        }
    }
    
}
